#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include "my_thread.h"
#include "ring_thread_list.h"


struct list* sleepq=NULL;
struct list* runq=NULL;
struct list* deadq=NULL;


thread_t current;

extern thread_t thread_self(void){	
	return current==NULL ? -1 : current->thread_id;
}
extern int thread_yield(){
	thread_t tmp=current;
	int i=0;
	if(current==NULL){
		thread_init();
	}	//on met current a l'arriere de la runq
	if(current->status==RUNNING){
		runq=shift(runq);
	}
	current=head(runq);
	display(runq);
	i =swapcontext(&(tmp->context),&(current->context));
	if(i==-1)
		fprintf(stdout,"impossible swap: ");
	return i;
}



void g(void *(*func)(void *),void ** retval,void * param){

	*retval=func(param);
}

extern int thread_create(thread_t* nthread, void *(*func)(void *), void *funcarg){
	if(current==NULL){
		thread_init();
	}
	thread_t newthread=malloc(sizeof(struct thread));
	newthread->thread_id=current->thread_id;
	current->thread_id++;
	newthread->status=RUNNING;
	ucontext_t* uc=&(newthread->context);
	ucontext_t previous=current->context;

	uc->uc_stack.ss_size = STACK_SIZE;
	uc->uc_stack.ss_sp = malloc(uc->uc_stack.ss_size);
	uc->uc_link = &previous;
	void** retval=malloc(sizeof(void*));
	getcontext(uc);
	makecontext(uc,(void (*)(void))g,3,func,retval,funcarg);
	*nthread=newthread;
	runq = insert(runq,newthread);
	int i=thread_yield();
	newthread->retval=*retval;
	return i;
}

int wake(thread_t t){
	//sleepq=remove_element (sleepq,t);
	runq=insert(runq,t);
	t->status=RUNNING;

	return 0;
}



extern int thread_join(thread_t t,void** retval){
	if(current==NULL){
		thread_init();
	}
	if(t==thread_self()){
		return -1;
	}
	while(!t->status==ENDED){
		thread_yield();
	}
	retval=&(t->retval);
	deadq = remove_element(deadq,t);
	runq = remove_element(runq,t);
	free(t);
	return 0;
}

void thread_init(){
	current=malloc(sizeof(struct thread));

	current->retval=NULL;
	current->status=RUNNING;
	current->execution_time=NULL;
	current->thread_id=0;
	runq = create(runq,current);//juste pour initialiser
	display(runq);
}
extern void thread_exit(void *retval){
	if(current==NULL){
		thread_init();
	}
	ucontext_t uc;
	current->retval=retval;
	getcontext(&uc);
	runq = remove_element(runq,current);
	deadq = insert(deadq,current);
	current->status=ENDED;
	current->context=*(current->context.uc_link);
	thread_yield();
	exit(0);
}

